package com.hmz.backend.consumer;



import cn.hutool.crypto.digest.DigestAlgorithm;
import cn.hutool.crypto.digest.Digester;
import cn.hutool.json.JSONUtil;
import com.hmz.backend.entity.User;
import com.hmz.backend.entity.Message;
import com.hmz.backend.mapper.ChatRoomMapper;
import com.hmz.backend.utils.PasswordEncoder;
import jakarta.websocket.*;
import jakarta.websocket.server.PathParam;
import jakarta.websocket.server.ServerEndpoint;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.ListOperations;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArraySet;

/**
 * websocket节点
 */
@Component
@ServerEndpoint("/chat/{token}/{chatId}")
public class WebSocketServer {
    private static ListOperations listOperations; //操作redis
    private static ChatRoomMapper chatRoomMapper;
    //当有一个client连接时就会实例化一个websocketserver 对象，所以操作redis的对象是个静态对象 ，以下面这种方式注入
    @Autowired
    public void setListOperations( RedisTemplate redisTemplate){
        WebSocketServer.listOperations = redisTemplate.opsForList();
    }
    @Autowired
    public void setChatRoomMapper(ChatRoomMapper chatRoomMapper){
        WebSocketServer.chatRoomMapper = chatRoomMapper;
    }
    //用户Id
    private int userId;
    //通过client连接的session对象，来主动通知client
    private Session session;
    //加密函数
    private static Digester  md5 = new Digester(DigestAlgorithm.MD5);
    //存放每个客户端对应的websocket对象
    private  static CopyOnWriteArraySet<WebSocketServer> webSocketSet = new CopyOnWriteArraySet<>();
    //存放每个链接
    private static ConcurrentHashMap<Integer,Session> sessionMap = new ConcurrentHashMap<>();
    //存放当前的用户Id和聊天Id
    private static ConcurrentHashMap<Integer,Integer> userIdChatIdMap = new ConcurrentHashMap<>();

    /**
     * 当websocket 链接上调用的方法
     * @author haomingze
     * @param session 自动给出
     * @param token 路径传参
     * @param chatId 聊天室的Id
     */
    @OnOpen
    public void onOpen(Session session, @PathParam("token") String token,@PathParam("chatId") int chatId){
        this.userId = Integer.parseInt(PasswordEncoder.uncode(token).substring(4));
        this.session = session;
        sessionMap.put(this.userId,this.session);
        userIdChatIdMap.put(this.userId,chatId);
        //System.out.println(this.userId+"+"+this.session);
        webSocketSet.add(this);
        System.out.println("WebSocket连接成功");
    }

    /**
     * 当server端接收到消息调用
     * @param inputMessage
     * @param session
     */
    @OnMessage
    public void onMessage(String inputMessage, Session session){
        //将接收到的json字符串转成对象
        Message message = JSONUtil.toBean(inputMessage, Message.class);
        int receiverId = message.getReceiverId();
        //将消息存进redis
        listOperations.rightPush("message"+receiverId,message);
        //发送消息
        sendMessage(message);
    }

    /**
     * 发送消息
     * @param message
     */
    public synchronized static void sendMessage(Message message){
        System.out.println("发送消息接口调用");
        int chatRoomId = message.getReceiverId();
        List<User> users = chatRoomMapper.getChatRoomUserList(chatRoomId);
        for(User user:users) //遍历聊天室中的所有用户表
            if(sessionMap.get(user.getUserId())!= null && userIdChatIdMap.get(user.getUserId()) == chatRoomId) //如果用户在线，并且在对应的聊天室下
                sessionMap.get(user.getUserId()).getAsyncRemote().sendText(JSONUtil.toJsonStr(message)); // 主动向client端发消息
    }

    /**
     * client与server端断开
     * @param token
     */
    @OnClose
    public void onClose(@PathParam("token") String token){
        sessionMap.remove(Integer.parseInt(PasswordEncoder.uncode(token).substring(4)));
        userIdChatIdMap.remove(Integer.parseInt(PasswordEncoder.uncode(token).substring(4)));
        webSocketSet.remove(this);
    }

    /**
     * 报错的时候打印错误
     * @param session
     * @param throwable
     */
    @OnError
    public void onError(Session session,Throwable throwable){
        System.out.println(throwable);
        System.out.println("websocket发生错误");
    }

}
